package stz.flash.main;

import net.rim.device.api.system.Backlight;
import net.rim.device.api.system.LED;
import net.rim.device.api.ui.Color;
import stz.flash.main.flashlight.model.Flashlight;
import stz.flash.option.Option;
import stz.flash.option.OptionScreen;
import stz.flash.option.PersistentData;
import stz.flash.option.TimerDurationChoiceField;
import stz.flash.option.flashlight.FlashlightOption;
import stz.flash.option.flashlight.LEDOption;
import util.DjDoneListener;
import util.DjObservable;



/**
 * Observer로 등록되어있는 클래스
 * @param TimerField
 * -----------------------------------
 * 
 * @param STOP_AND_START_into_strobe_mode 
 *  Stop = LED, Flashlight를 모두 정지시킨다
 *  Start = Backlight를 lock하는 동작만 하고 Flashlight, LED는 감시절에서 걸러낸다.
 * 
 * @param STOP_AND_START_into_flashlight_mode
 *  Stop = 그냥 넘어간다 (backlight lock만 풀음)
 * 	Start = 정상 작동한다.
 * 
 * @param STOP_AND_START_strobe_mode_change_listener
 * 	Stop = 그냥 넘어간다 (backlight lock만 풀음)
 * 	Start = Backlight를 lock하는 동작만 하고 Flashlight, LED는 감시절에서 걸러낸다.
 * 
 **/

public class FlashTimer extends DjObservable {

	//stop()에 사용
	public static final int STOP = 0;
	public static final int STOP_timer_end = 1;
	
	//stopAndStart()에 사용
	public static final int STOP_AND_START_led_toggle = 2;
	public static final int STOP_AND_START_backlight_toggle = 3;
	public static final int STOP_AND_START_flashlight_toggle = 4;

	public static final int STOP_AND_START_timer_reset = 5;
	public static final int STOP_AND_START_into_strobe_mode = 6;
	public static final int STOP_AND_START_into_flashlight_mode = 7;
	public static final int STOP_AND_START_strobe_mode_change_listener = 8;
	
	private static final FlashTimer instance = new FlashTimer();
	private static StartTimer timer = new StartTimer();
	
	public static FlashTimer getIns() {
		return instance;
	}

	public void stopAndStart(int stopType) {
		
		if (getThread() != null && getThread().isAlive()) {

			
			timer.stop(stopType, new DjDoneListener() {
		
				public void done() {
					timer.start(instance);
				}
			});

			
		} else {
			
			setStopType(stopType);
			timer.start(instance);
		}
	}

	public void stop(int stopType, DjDoneListener doneListener) {
		timer.stop(stopType, doneListener);
	}

	public String getStringOfRemainingTime() {
		return timer.getStringOfRemainingTime();
	}

	public static void setStopType(int stopType){
		StartTimer.setStopType(stopType);
	}
	
	public static Thread getThread() {
		return timer.getThread();
	}
}

class StartTimer {

	private static Thread timerThread;
	private static Thread backlightLockThread;
	private static String stringOfRemainingTime;
	private static int stopType;
	private static DjDoneListener doneListener;
	
	public void start(final FlashTimer flashTimer) {
		timerThread = new Thread() {
			public void run() {
				
				PersistentData.getPersistentDatabase().setTimerThreadState(true);///////
				
				try {
					startList();
					
					everySecond();

				} catch (InterruptedException e) {
				} finally {

					stopList();

					if (doneListener != null) {
						doneListener.done();
						doneListener = null;
					}
					
					PersistentData.getPersistentDatabase().setTimerThreadState(false);//////
				}
			}

			/*
			 * startList
			 */
			private void startList() {
				determineFlashApply();
				determineLedApply();
				lockBacklight();
			}
			private void determineFlashApply() {
				
				if (stopType == FlashTimer.STOP_AND_START_backlight_toggle 
						|| stopType == FlashTimer.STOP_AND_START_led_toggle
						|| stopType == FlashTimer.STOP_AND_START_timer_reset
						|| stopType == FlashTimer.STOP_AND_START_into_strobe_mode
						|| stopType == FlashTimer.STOP_AND_START_strobe_mode_change_listener)
					return;

				if (FlashlightOption.isCurrentFlashState()) {
					setCurrentFlashStateAndNotify(true);
				} else {
					setCurrentFlashStateAndNotify(false);
				}
			}
			private void determineLedApply() {
				if (stopType == FlashTimer.STOP_AND_START_led_toggle
						|| stopType == FlashTimer.STOP_AND_START_into_flashlight_mode) {

					if (stopType == FlashTimer.STOP_AND_START_into_flashlight_mode) 
						LEDOption.getIns().updateLatestLedStateWithStartupSet();

					if (LEDOption.isLatestLEDState())
						LED.setColorConfiguration(100, 0, Color.WHITE);
					else
						LED.setState(LED.STATE_OFF);
				}
			}
			private void lockBacklight() {

				if (backlightLockThread != null && backlightLockThread.isAlive())
					return;

				backlightLockThread = new Thread() {
					public void run() {
						try {

							while (true) {

								Backlight.enable(true, Backlight.getTimeoutDefault());
								Thread.sleep((Backlight.getTimeoutDefault() - 5) * 1000);

								if (unlockSituation()) 
									return;
							}
						} catch (InterruptedException e) {
						}
					}
					private boolean unlockSituation() {
						return ScreenHandle.getIns().isDisplayed() == false ||
								OptionScreen.isOptionDisplayed();
					}
				};
				backlightLockThread.start();
			}
			
			private void everySecond() throws InterruptedException {
				
				int second = Option.getIns().getTimerDuration().intValue();
			
				initRemainingTime(flashTimer, second);
				
				for (int i = 0; i < second; i++) {
					flashTimer.notifyObservers();
					subRemainingTime();
					sleep(1000);
				}
				setStopType(FlashTimer.STOP_timer_end);
				clearRemainingField();
			}
			private void initRemainingTime(FlashTimer flashTimer, int second) throws InterruptedException {
				if (TimerDurationChoiceField.INTEGER_OF_INTEGER_MAX_VALUE.intValue() == second) {
					stringOfRemainingTime = "unlimited";
					flashTimer.notifyObservers();
					sleep(Integer.MAX_VALUE);
				}
				stringOfRemainingTime = String.valueOf(second);
			}
			private void subRemainingTime() {
				stringOfRemainingTime = String.valueOf(Integer.parseInt(stringOfRemainingTime) - 1);
			}
			private void clearRemainingField() {
				stringOfRemainingTime = "";
				flashTimer.notifyObservers();
			}
			
			
			/*
			 * stopList
			 */
			void stopList() {
				determineFlashOff();
				determineLedOff();
				unlockBacklight();
			}
			private void determineFlashOff() {
				
				if (stopType == FlashTimer.STOP_AND_START_led_toggle
						|| stopType == FlashTimer.STOP_AND_START_backlight_toggle
						|| stopType == FlashTimer.STOP_AND_START_timer_reset
						|| stopType == FlashTimer.STOP_AND_START_into_flashlight_mode
						|| stopType == FlashTimer.STOP_AND_START_strobe_mode_change_listener)
					return;

				if (stopType == FlashTimer.STOP_AND_START_flashlight_toggle) {

					FlashlightOption.setCurrentFlashState(!FlashlightOption.isCurrentFlashState());
				}
				
				if (stopType == FlashTimer.STOP 
						|| stopType == FlashTimer.STOP_timer_end
						|| stopType == FlashTimer.STOP_AND_START_into_strobe_mode) {

					//Stop 상황이므로 특별히 stopList()에서 Notify시킴
					setCurrentFlashStateAndNotify(false);

					return;
				}
			}
			private void determineLedOff() {
			
				if (stopType == FlashTimer.STOP_AND_START_backlight_toggle
						|| stopType == FlashTimer.STOP_AND_START_flashlight_toggle
						|| stopType == FlashTimer.STOP_AND_START_timer_reset
						|| stopType == FlashTimer.STOP_AND_START_into_flashlight_mode
						|| stopType == FlashTimer.STOP_AND_START_strobe_mode_change_listener)
					return;
			
				if (stopType == FlashTimer.STOP 
						|| stopType == FlashTimer.STOP_timer_end
						|| stopType == FlashTimer.STOP_AND_START_into_strobe_mode) {
					LED.setState(LED.STATE_OFF);
					return;
				}
				
				if (stopType == FlashTimer.STOP_AND_START_led_toggle) {
					LEDOption.setLatestLEDState(!LEDOption.isLatestLEDState());
				}
			}
			private void unlockBacklight() {

				if (backlightLockThread == null || 
						backlightLockThread.isAlive() == false) 
					return;
				backlightLockThread.interrupt(); 

				if (OptionScreen.isOptionDisplayed())
					return;
				
				if (stopType == FlashTimer.STOP_timer_end) {
					Backlight.enable(false);
				} 
			}
			
			private void setCurrentFlashStateAndNotify(boolean on){

				FlashlightOption.setCurrentFlashState(on);

				FlashlightOption.getIns().notifyFlashlight();
				
				joinFlashlightThread(on);
			}
			private void joinFlashlightThread(boolean currentFlashState) {
				
				if (flashlightThreadJoinSituation(currentFlashState)) {
					try {
						if (Flashlight.getThread() != null)
							Flashlight.getThread().join();
					} catch (InterruptedException e) {
					}
				}
			}
			private boolean flashlightThreadJoinSituation(boolean currentFlashState) {
				return Option.getIns().getLatestMode() == Option.FLASHLIGHT_MODE 
						&& currentFlashState == false;
			}
		};
		timerThread.start();
	}

	public String getStringOfRemainingTime() {
		return StartTimer.stringOfRemainingTime;
	}

	public synchronized void stop(int stopType, DjDoneListener doneListener) {
		setStopType(stopType);
		StartTimer.doneListener = doneListener;
		if (timerThread == null || timerThread.isAlive() == false) 
			return;
		timerThread.interrupt();
	}
	
	public static void setStopType(int stopType){
		StartTimer.stopType = stopType;
	}

	public Thread getThread() {
		return timerThread;
	}
}